﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.Collections;
using System.Globalization;

namespace WPCoco.Helpers
{
    public static class PlistHelper
    {
        private static XElement ElementToXml(XDocument doc, object o)
        {
            XElement node = null;
            if (o == null) 
            {
                node = new XElement("string");
                node.Value = "";
                return node;
            }
            Type t = o.GetType();

            if (ReflectionHelper.IsInteger(t))
            {
                node = new XElement("integer");
                node.Value = o.ToString();
                return node;
            }
            if (ReflectionHelper.IsReal(t))
            {
                node = new XElement("real");
                node.Value = ReflectionHelper.GetRealValue(o);
                return node;
            }
            if (ReflectionHelper.IsBool(t))
            {
                node = new XElement(((bool)o) ? "true" : "false");
                return node;
            }
            if (ReflectionHelper.IsDateTime(t))
            {
                node = new XElement("date");
                //TODO: See format;
                node.Value = ((DateTime)o).ToString(CultureInfo.InvariantCulture);
                return node;
            }

            if (ReflectionHelper.IsDictionaryStringObject(t))
                return DictionaryToXml(doc, o);

            if (ReflectionHelper.IsArray(t))
                return ArrayToXml(doc, o);

            node = new XElement("string");
            node.Value = o.ToString();

            return node;
        }

        private static XElement DictionaryToXml(XDocument doc, object o)
        {
            IDictionary<string, object> dict = o as IDictionary<string, object>;
            if (dict == null) return null;
            XElement node = new XElement("dict");
            XElement knode;
            foreach (string key in dict.Keys)
            {
                knode = new XElement("key");
                knode.Value = key;
                node.Add(knode);
                node.Add(ElementToXml(doc, dict[key]));
            }
            return node;
        }

        private static XElement ArrayToXml(XDocument doc, object o)
        {
            IEnumerable array = o as IEnumerable;
            if (array == null) return null;
            XElement node = new XElement("array");
            foreach (object item in array)
                node.Add(ElementToXml(doc, item));
            return node;
        }

        public static object XmlToElememt(XElement elem)
        {
            string name = elem.Name.LocalName;
            string data = elem.Value;
            bool r;
            switch (name)
            {
                case "integer":
                    int int32;
                    r = int.TryParse(data, out int32);
                    if (r) return int32;
                    uint uint32;
                    r = uint.TryParse(data, out uint32);
                    if (r) return uint32;
                    long int64;
                    r = long.TryParse(data, out int64);
                    if (r) return int64;
                    ulong uint64;
                    r = ulong.TryParse(data, out uint64);
                    if (r) return uint64;
                    return null;
                case "real":
                    double d;
                    r = double.TryParse(data, NumberStyles.Float, CultureInfo.InvariantCulture, out d);
                    if (r) return d;
                    return null;
                case "true":
                    return true;
                case "false":
                    return false;
                case "date":
                    DateTime dt = DateTime.Parse(data);
                    return dt;
                case "dict":
                    return XmlToDictionary(elem);
                case "array":
                    return XmlToArray(elem);
                default:
                    return elem.Value;
            }
        }

        private static IDictionary<string, object> XmlToDictionary(XElement elem)
        {
            XElement knode = null;
            XElement vnode;
            IDictionary<string, object> dict = new Dictionary<string, object>();
            int i = 0;
            foreach (var n in elem.Elements())
            {
                if (i % 2 == 0)
                {
                    knode = n as XElement;
                }
                else
                {
                    vnode = n as XElement;
                    dict[knode.Value] = XmlToElememt(vnode);
                }
                i++;
            }
            return dict;
        }

        private static object[] XmlToArray(XElement elem)
        {
            XElement vnode;
            List<object> list = new List<object>();
            foreach (var n in elem.Elements()) 
            {
                vnode = n as XElement;
                list.Add(XmlToElememt(vnode));
            }
            object[] array = new object[list.Count];
            list.CopyTo(array, 0);
            return array;
        }

        public static void SaveObjectToFile(object o, string fileName)
        {
            XDocument doc = new XDocument(new XDeclaration("1.0", "UTF-8", null));
            //doc.Add(new XDocumentType("plist",
            //                          "-//Apple Computer//DTD PLIST 1.0//EN",
            //                          "http://www.apple.com/DTDs/PropertyList-1.0.dtd",
            //                          null));

            XElement plist = new XElement("plist");
            plist.Add(new XAttribute("version", "1.0"));
            plist.Add(ElementToXml(doc, o));
            doc.Add(plist);
            Stream stream = File.Create(fileName);
            doc.Save(stream, SaveOptions.None);
            stream.Close();
        }

        public static void SaveObjectToIsolatedFile(object o, string filePath) 
        {
            XDocument doc = new XDocument(new XDeclaration("1.0", "UTF-8", null));
            //doc.Add(new XDocumentType("plist",
            //                          "-//Apple Computer//DTD PLIST 1.0//EN",
            //                          "http://www.apple.com/DTDs/PropertyList-1.0.dtd",
            //                          null));

            XElement plist = new XElement("plist");
            plist.Add(new XAttribute("version", "1.0"));
            plist.Add(ElementToXml(doc, o));
            doc.Add(plist);
            FileStream stream = IsolatedStorageHelper.OpenWrite(filePath);
            doc.Save(stream);
            stream.Close();
        }

        public static object LoadObjectFromFile(string fileName)
        {
            Stream stream = File.OpenRead(fileName);
            XDocument doc = XDocument.Load(stream);
            stream.Close();
            return LoadObjectFromDocument(doc);
        }

        public static object LoadObjectFromIsolatedFile(string filePath)
        {
            FileStream stream = IsolatedStorageHelper.OpenRead(filePath);
            XDocument doc = XDocument.Load(stream);
            stream.Close();
            return LoadObjectFromDocument(doc);
        }

        public static object LoadObjectFromDocument(XDocument doc)
        {
            if (doc == null || doc.Root == null || doc.Root.FirstNode == null) return null;
            object o = XmlToElememt((XElement)doc.Root.FirstNode);
            return o;
        }
    }
}